package com.podium.comment.utils;


/**
 * 基于时间生成顺序ID的工具类（简化版Snowflake算法）
 * @author 邵文泽
 */
public class SequentialIdGenerator {

    // 起始时间戳（2023-01-01 00:00:00）
    private static final long START_TIMESTAMP = 1672531200000L;

    // 机器ID所占的位数
    private static final long MACHINE_ID_BITS = 5L;

    // 序列号所占的位数
    private static final long SEQUENCE_BITS = 12L;

    // 机器ID的最大值
    private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);

    // 序列号的最大值
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    // 时间戳左移位数（机器ID + 序列号）
    private static final long TIMESTAMP_SHIFT = MACHINE_ID_BITS + SEQUENCE_BITS;

    // 机器ID左移位数（序列号）
    private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;

    // 机器ID（0-31）
    private final long machineId;

    // 序列号（0-4095）
    private long sequence = 0L;

    // 上次生成ID的时间戳
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     *
     * @param machineId 机器ID（0-31）
     */
    public SequentialIdGenerator(long machineId) {
        if (machineId < 0 || machineId > MAX_MACHINE_ID) {
            throw new IllegalArgumentException("Machine ID must be between 0 and " + MAX_MACHINE_ID);
        }
        this.machineId = machineId;
    }

    /**
     * 生成下一个ID
     *
     * @return 返回生成的ID
     */
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate ID.");
        }

        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0) {
                // 当前毫秒的序列号已用完，等待下一毫秒
                timestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        return ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
                | (machineId << MACHINE_ID_SHIFT)
                | sequence;
    }

    /**
     * 等待下一毫秒
     *
     * @param lastTimestamp 上次生成ID的时间戳
     * @return 返回当前时间戳
     */
    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

}
